home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / ms-0.07 / mslaved / mslaved.c < prev    next >
C/C++ Source or Header  |  1995-06-26  |  11KB  |  436 lines

  1. /* mslaved.c - MandelSpawn computation server deamon */
  2.  
  3. /*  
  4.    This file is part of MandelSpawn, a network Mandelbrot program.
  5.  
  6.     Copyright (C) 1990-1993 Andreas Gustafsson
  7.  
  8.     MandelSpawn is free software; you can redistribute it and/or modify
  9.     it under the terms of the GNU General Public License, version 1,
  10.     as published by the Free Software Foundation.
  11.  
  12.     MandelSpawn is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License,
  18.     version 1, along with this program; if not, write to the Free 
  19.     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <sys/ioctl.h>
  27. #include <signal.h>
  28. #ifdef HAVE_SYSLOG
  29. #include <syslog.h>
  30. #endif
  31.  
  32. #include "datarep.h"
  33. #include "ms_real.h"
  34. #include "ms_ipc.h"
  35. #include "ms_job.h"
  36. #include "ms_real.c"
  37.  
  38. /*
  39.   Note that the timeout default below is overridden by mslavedc so that 
  40.   manually started servers will persist throughout a typical session. 
  41. */
  42. #define DEFAULT_SLAVE_TIMEOUT (60)   /* by default time out in 60 seconds */
  43. #define DEFAULT_NICE (10)         /* use nice 10 by default */
  44.  
  45. char *me;                 /* name of program */
  46. int timeout = DEFAULT_SLAVE_TIMEOUT;    /* timeout */
  47. int niceval = DEFAULT_NICE;         /* nice value */
  48. int use_sockets = 1;
  49.  
  50. /* Log an error message and exit */
  51. error(s)
  52.      char *s;
  53. {
  54. #ifdef HAVE_SYSLOG
  55.   syslog(LOG_ERR, "%s: %s (%m)", me, s);
  56. #endif
  57.   exit(1);
  58. }
  59.  
  60. #ifdef HAVE_SOCKETS
  61.  
  62. #define RECV(fd, data, len, opts, name, namelen) \
  63.   (use_sockets ? \
  64.      recvfrom(fd, data, len, opts, name, namelen) : \
  65.      read(fd, data, len))
  66.  
  67. #define SEND(fd, data, len, opts, name, namelen) \
  68.   (use_sockets ? \
  69.      sendto(fd, data, len, opts, name, namelen) : \
  70.      write(fd, data, len))
  71.  
  72. #else /* !HAVE_SOCKETS */
  73.  
  74. #define RECV(fd, data, len, opts, name, namelen) \
  75.   read(fd, data, len) 
  76.  
  77. #define SEND(fd, data, len, opts, name, namelen) \
  78.   write(fd, data, len) 
  79.  
  80. #endif /* !HAVE_SOCKETS */
  81.  
  82.  
  83. /* Decode a single parameter */
  84.  
  85. real decode_parm(p)
  86.      char **p;
  87. {
  88.   real r = net_to_real(ntohli(*((netreal *) *p)));
  89.   *p += sizeof(netreal);
  90.   return r;
  91. }
  92.  
  93. unsigned int mandelbrot(parms, maxiter, flags)
  94.     real *parms;
  95.     unsigned int maxiter;
  96.     int flags;
  97. {
  98.       register real x_re, x_im;
  99.       register real c_re, c_im;
  100.       register real xresq, ximsq;
  101.       complex c0;
  102.       complex z0;
  103.       real old_re = zero_real(), old_im = zero_real();
  104.       int show_interior;
  105.       unsigned int count;
  106.  
  107.       show_interior = !!(flags & MS_OPT_INTERIOR);
  108.  
  109.       c0.re = parms[0];
  110.       c0.im = parms[1];
  111.       z0.re = parms[2];
  112.       z0.im = parms[3];
  113.  
  114.       c_re=c0.re; c_im=c0.im;
  115.       x_re=z0.re; x_im=z0.im;
  116.  
  117.       /* The following loop is where the Real Work gets done. */
  118.       count=0;
  119.       while(count < maxiter-1)
  120.       {    
  121.         /*
  122.       The following if statement implements limit cycle detection
  123.       to speed up calculation of areas inside the Mandelbrot set. 
  124.       Unfortunately, it also slows down the calculation of other areas,
  125.       to the degree that it probably pays off only when doing shallow
  126.       zooms with large black areas on machines with slow multiply
  127.       instructions.  Therefore it is now disabled by default.
  128.     */
  129.  
  130. #ifdef CYCLE_DETECT
  131.     if((count & (count-1)) == 0)
  132.     { /* "count" is zero or a power of two; save the current position */
  133.       old_re=x_re;
  134.       old_im=x_im;
  135.     }
  136.     else
  137.     { 
  138.           /*
  139.         Check if we have returned to a previously saved position; 
  140.         if so, the iteration has converged to a limit cycle => we 
  141.         are inside the Mandlebrot set and need iterate no further.
  142.       */
  143.       if(x_re==old_re && x_im==old_im)
  144.       { if(! show_interior)
  145.           count = maxiter-1;
  146.         break;
  147.       }
  148.     }
  149. #endif
  150.     /* 
  151.       This is the familiar "z := z^2 + c; abort if |z| > 2"
  152.       Mandelbrot iteration, with the arithmetic operators hidden
  153.       in macros so that the same code can be compiled for either
  154.       fixed-point or floating-point arithmetic.
  155.       The macros (mul_real(), etc.) are defined in ms_real.h. 
  156.     */
  157.     xresq=mul_real(x_re, x_re);
  158.     ximsq=mul_real(x_im, x_im);
  159.     if(gteq_real(add_real(xresq, ximsq), four_real()))
  160.       break;
  161.     x_im=add_real(twice_mul_real(x_re, x_im), c_im);
  162.     x_re=add_real(sub_real(xresq, ximsq), c_re);
  163.     count++;
  164.       }
  165.   return count;
  166. }
  167.  
  168. /* 
  169.   An alternative fractal.  This code is turned of for now because the
  170.   protocol doesn't yet support multiple fractal types.  
  171. */
  172.  
  173. #ifdef HENON
  174. unsigned int henon(parms, maxiter, flags)
  175.   real *parms;
  176.   unsigned int maxiter;
  177.   int flags;
  178. {
  179.   register real c0, c1; /* coefficients */
  180.   register real x0, x1, x2; /* states */
  181.   
  182.   unsigned int count;
  183.   
  184.   c0 = parms[0];
  185.   c1 = parms[1];
  186.   x1 = parms[2];
  187.   x2 = parms[3];
  188.     
  189.   count=0;
  190.   while(count < maxiter-1)
  191.   { 
  192.     x0 = add_real(one_real(),
  193.           add_real(mul_real(c0, x2),
  194.                mul_real(c1, mul_real(x1,x1))));
  195.     x2 = x1;
  196.     x1 = x0;
  197.     
  198.     if(x0 > int_to_real(10) || x0 < int_to_real(-10))
  199.       break;
  200.     
  201.     count++;
  202.   }
  203.   return count;
  204. }
  205. #endif /* HENON */
  206.  
  207. #define MAX_PARMS 32
  208.  
  209. /*
  210.   Do the actual calculation and encode the results in an reply message;
  211.   return the size of the message (in bytes) 
  212. */
  213.  
  214. unsigned int calculate(in, out)
  215.      Message *in, *out;
  216. { int julia;         /* true if calculating a Julia set */
  217.   int show_interior;     /* true if displaying interior structure */
  218.   complex delta;
  219.   
  220.   int xc, yc, xmin, xmax, xsize, ymin, ymax, ysize;
  221.   unsigned int maxiter;
  222.   unsigned long mi_count=0;
  223.   unsigned int datasize;
  224.   int flags;
  225.   ms_job *job;
  226.   char *p;        /* untyped pointer to parameter block in message */
  227.   int x_parm_no, y_parm_no; /* indices of parameters to vary with x/y coord */ 
  228.   real *varx, *vary;    /* pointers to same */
  229.   real initial_varx;    
  230.   real parm_buf[MAX_PARMS];
  231.   int n_parms;
  232.   int i;
  233.  
  234. #define parms parm_buf
  235.   
  236.   job=(ms_job *) in->whip.data;
  237.   
  238.   /* check that the format is supported */
  239.   if(ntohs(in->whip.header.format) != DATA_FORMAT)
  240.     return(0);
  241.  
  242.   /* convert values in the message to host byte order and precalculate some */
  243.   /* useful values */
  244.   xmin=ntohs(job->s.x);
  245.   xsize=ntohs(job->s.width);
  246.   xmax=xmin+xsize;
  247.   ymin=ntohs(job->s.y);
  248.   ysize=ntohs(job->s.height);
  249.   ymax=ymin+ysize;
  250.  
  251.   maxiter = ntohli(job->j.iteration_limit);
  252.  
  253.   datasize = (maxiter > 256) ?
  254.     ((char *)(&(out->reply.data.shorts[xsize*ysize]))
  255.      -(char *)(&(out->reply))) :
  256.        ((char *)(&(out->reply.data.chars[xsize*ysize]))
  257.     -(char *)(&(out->reply)));
  258.   
  259.   /*
  260.     Perform a simple sanity check to avoid getting into semi-infinite 
  261.     loops because of malicious or erroneous messages.
  262.   */
  263.   if(datasize > MAX_DATAGRAM) 
  264.     error("data too large");
  265.  
  266.   /* 
  267.     Make sure the iteration counts can be represented in the result
  268.     packet; 65536 iterations can still take a long time and if we
  269.     keep calculating the same packet for too long the timeout alarm will
  270.     get us.  Those who need lots of iterations can use smaller chunks.
  271.   */
  272.   if(maxiter >= 65536)
  273.     error("iteration count too large");
  274.  
  275.   flags = ntohs(job->j.flags);
  276.   julia = ntohs(job->j.julia);
  277.  
  278.   if(julia)
  279.   {
  280.     n_parms = 4;
  281.     x_parm_no = 2; /* z0.re */
  282.     y_parm_no = 3; /* z0.im */
  283.   }
  284.   else /* Mandelbrot */
  285.   {
  286.     n_parms = 4;
  287.     x_parm_no = 0; /* c0.re */
  288.     y_parm_no = 1; /* c0.im */
  289.   }
  290.  
  291.   /* point to beginning of parameters */
  292.   p = (char *) &job->j.corner.re;
  293.  
  294.   /* convert each parameter from network format to computational format */
  295.   for(i=0; i<n_parms; i++)
  296.     parm_buf[i] = decode_parm(&p);
  297.  
  298.   /* set up pointers to the parameters that correspond to x and y*/
  299.   varx = &parm_buf[x_parm_no];
  300.   vary = &parm_buf[y_parm_no];
  301.  
  302.   delta.re = decode_parm(&p);
  303.   delta.im = decode_parm(&p);
  304.  
  305.   /* take the chunk offset into account */
  306.   (*varx) = add_real((*varx), mul_real_int(delta.re, xmin));
  307.   (*vary) = add_real((*vary), mul_real_int(delta.im, ymin));
  308.  
  309.   /* save initial x coordinate for reuse on subsequent scanlines */
  310.   initial_varx = (*varx);
  311.  
  312.   for(yc=ymin; yc<ymax; yc++)
  313.   { 
  314.     (*varx) = initial_varx;
  315.  
  316.     for(xc=xmin; xc<xmax; xc++)
  317.     {
  318. #ifdef HENON
  319.       unsigned int count = henon(parm_buf, maxiter, flags);
  320. #else
  321.       unsigned int count = mandelbrot(parm_buf, maxiter, flags);
  322. #endif
  323.       
  324.       if(maxiter > 256)
  325.     out->reply.data.shorts[(yc-ymin)*xsize+(xc-xmin)]=htons(count);
  326.       else
  327.     out->reply.data.chars[(yc-ymin)*xsize+(xc-xmin)]=count;
  328.       mi_count+=count;
  329.  
  330.       (*varx) = add_real((*varx), delta.re);
  331.     }
  332.     (*vary) = add_real((*vary), delta.im);
  333.   }
  334.   out->reply.mi_count=htonl(mi_count);
  335.   return(datasize);
  336. }
  337.  
  338.  
  339. void serve()
  340. { Message in;
  341.   Message out;
  342.   int isock = 0;
  343.   int osock = use_sockets ? 0 : 1;
  344.  
  345.   NET_ADDRESS oname;
  346.  
  347.   while(1)
  348.   { unsigned int bytes;
  349.     int onamelen=sizeof(oname);
  350.     int version;
  351.     if(timeout != 0)
  352.       alarm(timeout);
  353.     /* receive from anywhere, save the address of the caller in oname */
  354.     if(RECV(isock, (char *)&in, sizeof(in), 0,
  355.         (struct sockaddr *) &oname, &onamelen) < 0) 
  356.       error("receiving datagram packet");
  357.     version=ntohs(in.generic.header.version);
  358.     if(ntohs(in.generic.header.magic)==MAGIC && version==VERSION)
  359.     { switch(ntohs(in.generic.header.type))
  360.       {    case WHIP_MESSAGE:
  361.       /* copy the header and id structures as such while still in */
  362.       /* network byte order (not strictly portable but probably works) */
  363.       out.reply.header=in.whip.header;
  364.       out.reply.id=in.whip.id;
  365.       /* just the message type needs to be changed */
  366.       out.reply.header.type=htons(REPLY_MESSAGE);
  367.       /* calculate() sets the data and mi_count fields */
  368.       bytes=calculate(&in, &out);
  369.       if(bytes)
  370.         if(SEND(osock, (char *)&out, (int)bytes, 0,
  371.               (struct sockaddr *) &oname, sizeof(oname)) < 0)
  372.           error("sending calculated data");
  373.       break;
  374.     case WHO_R_U_MESSAGE:
  375.       oname.sin_port=in.who.port; /* in network byte order already */
  376.       out.iam.header=in.who.header;
  377.       out.iam.pid=htons(getpid());
  378.       if(SEND(osock, (char *)&out, sizeof(IAmMessage), 0,
  379.             (struct sockaddr *) &oname, sizeof(oname)) < 0)
  380.         error("sending response to pid inquiry");
  381.       break;
  382.     default: ; /* ignore other messages */
  383.       }
  384.     }
  385.   }
  386. }
  387.  
  388.  
  389. /*
  390.   This function is called when we get a SIGALRM so that we die gracefully 
  391.   with exit status 0; otherwise inetd would log a message about us getting
  392.   an alarm signal.
  393. */
  394.  
  395. int die()
  396. { exit(0);
  397. }
  398.  
  399. int main(argc, argv)
  400.      int argc; char **argv;
  401. { static char usage_msg[] = "usage: mslaved [ -options ] mslaved\n";
  402.   me=argv[0];
  403.   signal(SIGALRM, die);
  404.   while(--argc)
  405.   { char *s = *++argv;
  406.     if(*s++ != '-')
  407.       goto usage;
  408.     switch(*s++)
  409.     { 
  410.     case 'n': /* nice */
  411.       niceval=atoi(*s ? s : (--argc, *++argv));
  412.       break;
  413.     case 't': /* timeout */
  414.       timeout=atoi(*s ? s : (--argc, *++argv));
  415.       break;
  416.     case 'i': /* accept for backwards compatibility but ignore */
  417.       break;
  418.     case 'p': /* pipe mode */
  419.       use_sockets = 0;
  420.       break;
  421.     default: 
  422.       goto usage;
  423.     }
  424.   }
  425.   nice(niceval);
  426.   serve(); /* never returns */
  427.   
  428.  usage:
  429.   /*
  430.     Try to avoid pulling in printf from the library 
  431.     (but syslog probably does that anyway...) 
  432.   */
  433.   write(2, usage_msg, sizeof(usage_msg)-1);
  434.   exit(1);
  435. }
  436.